﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="JobsAdminController.cs" company="DST Nexdox">
//   Copyright (c) DST Nexdox. All rights reserved.
// </copyright>
// <summary>
//   Defines the JobsAdminController type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace DocProcessingWorkflow.Controllers
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Web.Mvc;
  using DocProcessingWorkflow.Constants;
  using DocProcessingWorkflow.Filters;
  using DocProcessingWorkflow.Models.JobsAdmin;
  using DocProcessingWorkflow.Models.MailSortSetting;
  using Entities;
  using Exceptions;
  using Logging;
  using ServiceInterfaces;

  [AuthorizeLoggedInUser]
  [Authorize(Roles = Role.DSTAdmin)]
  public class JobsAdminController : BaseController
  {
    private readonly IUserService userService;

    private readonly IManCoService manCoService;

    private readonly IManCoDocService manCoDocService;

    private readonly IMediaDefinitionService mediaDefinitionService;

    private readonly IMediaDefinitionAuditService mediaDefinitionAuditService;

    private readonly IMediaStockService mediaStockService;

    private readonly IStockService stockService;

    private readonly IIndexDefinitionService indexDefinitionService;

    private readonly IMailSortSettingService mailSortSettingService;

    public JobsAdminController(
      ILogger logger,
      IAppEnvironmentService appEnvironmentService,
      IUserService userService,
      IManCoService manCoService,
      IManCoDocService manCoDocService,
      IMediaDefinitionService mediaDefinitionService,
      IMediaDefinitionAuditService mediaDefinitionAuditService,
      IMediaStockService mediaStockService,
      IStockService stockService,
      IMailSortSettingService mailSortSettingService,
      IIndexDefinitionService indexDefinitionService)
      : base(logger, appEnvironmentService, userService)
    {
      this.userService = userService;
      this.manCoService = manCoService;
      this.manCoDocService = manCoDocService;
      this.mediaDefinitionService = mediaDefinitionService;
      this.mediaDefinitionAuditService = mediaDefinitionAuditService;
      this.mediaStockService = mediaStockService;
      this.stockService = stockService;
      this.mailSortSettingService = mailSortSettingService;
      this.indexDefinitionService = indexDefinitionService;
    }

    private enum UpdateStationNumbersValidationResultFailure
    {
      NoAssignmentsSubmitted,

      StockTypeCannotBeUpdated,

      StationNumberInvalid,

      StationNumberDuplicated
    }

    [Route("JobsAdmin/Index")]
    public ViewResult Index()
    {
      var model = new JobsAdminViewModel();

      ApplicationUser currentUser = this.userService.GetApplicationUser();
      IList<ManagementCompany> manCos = this.manCoService.GetManCos(currentUser.Id);
      List<MailsortSetting> mailSortSettings = this.mailSortSettingService.GetMailsortSettings();

      model.AddManCos(manCos);
      model.AddMailSortSettings(mailSortSettings);

      return this.View(model);
    }

    [Route("JobsAdmin/GetMediaDefinitionsAjax")]
    public PartialViewResult GetMediaDefinitionsAjax(
      String environment,
      String manCoCode,
      String documentTypeCode,
      String documentTypeDescription,
      Int32 page = 1)
    {
      ManCoDoc manCoDoc = this.manCoDocService.GetManCoDoc(
        manCoCode,
        documentTypeCode,
        documentTypeDescription,
        environment);

      PagedResult<MediaDefinitionEntity> mediaDefinitions = this.mediaDefinitionService.GetByManCoDoc(
        page,
        10,
        manCoDoc.ManCoDocID);

      var model = new JobsAdminMediaDefinitionsViewModel();

      model.AddMediaDefinitions(mediaDefinitions);

      foreach (MediaDefinitionEntity mediaDefinition in mediaDefinitions.Results)
      {
        List<MediaStockEntity> mediaStocks = this.mediaStockService.GetByMediaDefinition(mediaDefinition.MediaDefID);
        model.AddMediaStocksForDefinition(mediaDefinition.MediaDefID, mediaStocks);
      }

      return this.PartialView(model);
    }

    [Route("JobsAdmin/GetMediaDefinitionAuditsAjax")]
    public PartialViewResult GetMediaDefinitionAuditsAjax(Int32 mediaDefinitionId)
    {
      var model = new MediaDefinitionAuditsViewModel();

      List<MediaDefinitionAuditEntity> audits = this.mediaDefinitionAuditService.GetAudits(mediaDefinitionId);

      model.AddAudits(audits);

      return this.PartialView(model);
    }

    [Route("JobsAdmin/UpdatePrintFlag")]
    [HttpPost]
    public JsonResult UpdatePrintFlag(Int32 mediaDefinitionId, Boolean print)
    {
      this.mediaDefinitionService.UpdatePrintFlag(mediaDefinitionId, print);

      return this.Json(new { Success = true });
    }

    [Route("JobsAdmin/UpdateMailSortSetting")]
    [HttpPost]
    public JsonResult UpdateMailSortSetting(Int32 mediaDefinitionId, Int32 mailSortSettingId)
    {
      this.indexDefinitionService.UpdateMailSortSetting(mediaDefinitionId, mailSortSettingId);
      return this.Json(new { Success = true });
    }

    [Route("JobsAdmin/AssignmentStockAjax")]
    public PartialViewResult AssignmentStockAjax(Int32 mediaDefinitionId, String stockType, String filter)
    {
      List<MediaStockEntity> mediaStocks =
        this.mediaStockService.GetByMediaDefinition(mediaDefinitionId).Where(ms => ms.StockType == stockType).ToList();

      List<StockEntity> availableStock = this.GetAvailableStockItems(
        stockType,
        mediaStocks.Select(s => s.StockId).ToList(),
        filter);

      var model = new StockAssignmentViewModel
                    {
                      MediaDefinitionId = mediaDefinitionId,
                      StockType = stockType,
                      AllowEditingStationNumbers = !this.StationNumbersFixed(stockType),
                      DisplayStockType = this.GetStockTypeDisplayText(stockType),
                      DisplayStockTypeSingular =
                        this.GetStockTypeDisplayTextSingular(stockType),
                      Filter = filter
                    };

      model.AddExistingStock(mediaStocks);
      model.AddAvailableStock(availableStock);

      return this.PartialView(model);
    }

    [Route("JobsAdmin/UpdateStationNumbersValidityCheck")]
    [HttpPost]
    public JsonResult UpdateStationNumbersIsAvailable(
      Int32 mediaDefinitionId,
      String stockType,
      List<StationNumberAssignmentViewModel> assignments)
    {
      List<MediaStockEntity> existingMediaStockItems;

      UpdateStationNumbersValidationResult validityCheckResult =
        this.UpdateStationNumbersValidityCheck(mediaDefinitionId, stockType, assignments, out existingMediaStockItems);

      Boolean isValid = validityCheckResult.Result;

      return this.Json(new { Result = isValid });
    }

    [Route("JobsAdmin/UpdateStationNumbers")]
    [HttpPost]
    public PartialViewResult UpdateStationNumbers(
      Int32 mediaDefinitionId,
      String stockType,
      List<StationNumberAssignmentViewModel> assignments)
    {
      List<MediaStockEntity> existingMediaStockItems;

      UpdateStationNumbersValidationResult updateStationNumbersValidationResult =
        this.UpdateStationNumbersValidityCheck(mediaDefinitionId, stockType, assignments, out existingMediaStockItems);

      if (updateStationNumbersValidationResult.Result)
      {
        List<MediaStockEntity> updatedMediaStockItems = this.PerformUpdateStationNumbers(
          mediaDefinitionId,
          existingMediaStockItems,
          assignments);

        // Use StockAssignmentViewModel to generate the corresponding view model for the existing stock items
        var model = new StockAssignmentViewModel();
        model.AddExistingStock(updatedMediaStockItems);

        return this.PartialView("AssignmentExistingStockAjax", model.ExistingStock);
      }

      this.RaiseExceptionForFailure(updateStationNumbersValidationResult.Failure, stockType);
      throw new Exception("Exception should have already been raised");
    }

    [Route("JobsAdmin/RemoveMediaStock")]
    [HttpPost]
    public PartialViewResult RemoveMediaStock(
      Int32 mediaDefinitionId,
      String stockType,
      Int32 mediaStockId,
      String existingFilter)
    {
      List<MediaStockEntity> mediaStockItems =
        this.mediaStockService.GetByMediaDefinition(mediaDefinitionId).Where(ms => ms.StockType == stockType).ToList();

      MediaStockEntity itemToDelete = mediaStockItems.SingleOrDefault(ms => ms.MediaStockId == mediaStockId);
      if (itemToDelete == null)
      {
        throw new Exception("Item not already assigned");
      }

      this.mediaStockService.RemoveMediaStock(mediaStockId);

      mediaStockItems.Remove(itemToDelete);

      List<StockEntity> availableItems = this.GetAvailableStockItems(
        stockType,
        mediaStockItems.Select(s => s.StockId).ToList(),
        existingFilter);

      var model = new StockAssignmentViewModel
                    {
                      MediaDefinitionId = mediaDefinitionId,
                      StockType = stockType,
                      AllowEditingStationNumbers = !this.StationNumbersFixed(stockType),
                      DisplayStockType = this.GetStockTypeDisplayText(stockType),
                      DisplayStockTypeSingular =
                        this.GetStockTypeDisplayTextSingular(stockType),
                      Filter = existingFilter
                    };

      model.AddExistingStock(mediaStockItems);
      model.AddAvailableStock(availableItems);

      return this.PartialView("AssignmentStockAjax", model);
    }

    [Route("JobsAdmin/AddMediaStockIsAvailable")]
    [HttpPost]
    public JsonResult AddMediaStockIsAvailable(
      Int32 mediaDefinitionId,
      String stockType,
      IEnumerable<AssignStockViewModel> stockToAdd)
    {
      List<MediaStockEntity> existingMediaStockItems;

      UpdateStationNumbersValidationResult validityCheck = this.AddMediaStockValidityCheck(
        mediaDefinitionId,
        stockType,
        stockToAdd,
        out existingMediaStockItems);

      Boolean isValid = validityCheck.Result;

      return this.Json(new { Result = isValid });
    }

    [Route("JobsAdmin/AddMediaStock")]
    [HttpPost]
    public PartialViewResult AddMediaStock(
      Int32 mediaDefinitionId,
      String stockType,
      List<AssignStockViewModel> stockToAdd,
      String existingFilter)
    {
      List<MediaStockEntity> existingMediaStockItems;

      UpdateStationNumbersValidationResult validityCheck = this.AddMediaStockValidityCheck(
        mediaDefinitionId,
        stockType,
        stockToAdd,
        out existingMediaStockItems);

      if (validityCheck.Result)
      {
        List<MediaStockEntity> mediaStockItems = this.PerformAddMediaStock(
          mediaDefinitionId,
          existingMediaStockItems,
          stockToAdd);

        List<StockEntity> availableItems = this.GetAvailableStockItems(
          stockType,
          mediaStockItems.Select(s => s.StockId).ToList(),
          existingFilter);

        var model = new StockAssignmentViewModel
                      {
                        MediaDefinitionId = mediaDefinitionId,
                        StockType = stockType,
                        AllowEditingStationNumbers = !this.StationNumbersFixed(stockType),
                        DisplayStockType = this.GetStockTypeDisplayText(stockType),
                        DisplayStockTypeSingular =
                          this.GetStockTypeDisplayTextSingular(stockType),
                        Filter = existingFilter
                      };

        model.AddExistingStock(mediaStockItems);
        model.AddAvailableStock(availableItems);

        return this.PartialView("AssignmentStockAjax", model);
      }

      this.RaiseExceptionForFailure(validityCheck.Failure, stockType);
      throw new Exception("Exception should have already been raised");
    }

    [Route("JobsAdmin/CreateStock")]
    [HttpGet]
    public ViewResult CreateStock(String stockType)
    {
      // Validate the stockType, an exception will be raised if it's invalid
      this.stockService.GetStockTypeId(stockType);

      var model = new CreateStockViewModel { StockType = stockType, Facing = '0', Colour = "White" };

      if (this.AreStockCodesRestricted(stockType))
      {
        model.CodesAreRestricted = true;
        model.AvailableCodes.AddRange(this.GetAvailableStockCodes(stockType));
      }

      return this.View(model);
    }

    [Route("JobsAdmin/CreateStock")]
    [HttpPost]
    public JsonResult CreateStock(CreateStockViewModel model)
    {
      List<String> validationFailures = this.ValidateCreateStockViewModel(model);

      if (validationFailures.Any())
      {
        return this.Json(new { Success = false, Failures = validationFailures });
      }

      Int32 stockTypeId = this.stockService.GetStockTypeId(model.StockType);

      try
      {
        StockEntity newStock = this.stockService.CreateStock(
          stockTypeId,
          model.Code,
          model.Description,
          model.Weight,
          model.SizeX,
          model.SizeY,
          model.Facing,
          model.Colour);

        return this.Json(new { Success = true, newStock.StockId });
      }
      catch (DocProcessingException ex)
      {
        const String DuplicateItemFound = "Duplicate stock item found";
        if (ex.Message == DuplicateItemFound)
        {
          var failures = new List<String> { DuplicateItemFound };
          return this.Json(new { Success = false, Failures = failures });
        }

        throw;
      }
    }

    [Route("JobsAdmin/MailSortSettings")]
    public ViewResult MailSortSettings()
    {
      List<MailsortSetting> mailSortSettings = this.mailSortSettingService.GetMailsortSettings();
      var model = new MailSortSettingsViewModel();
      model.AddSettings(mailSortSettings);
      return this.View(model);
    }

    [Route("JobsAdmin/AddMailSortSetting")]
    public ViewResult AddMailSortSetting()
    {
      return this.View();
    }

    [Route("JobsAdmin/AddMailSortSetting")]
    [HttpPost]
    public ActionResult AddMailSortSetting(MailSortSettingViewModel mailSortSetting)
    {
      if (ModelState.IsValid)
      {
        this.mailSortSettingService.AddMailSortSetting(
          mailSortSetting.TNTDepot,
          mailSortSetting.TNTMailingHouse,
          mailSortSetting.TNTClientCode,
          mailSortSetting.TNTJobReference,
          mailSortSetting.NexdoxMailsortService,
          mailSortSetting.Largeletter,
          mailSortSetting.FixedWeight_NoOuter);

        return this.RedirectToAction("MailSortSettings");
      }

      List<String> errors =
        this.ModelState.Values
        .SelectMany(x => x.Errors)
        .Select(x => x.ErrorMessage)
        .ToList();

      this.TempData["errorMessages"] = errors;

      return this.View();
    }

    private List<String> ValidateCreateStockViewModel(CreateStockViewModel model)
    {
      var validationFailures = new List<String>();

      if (String.IsNullOrWhiteSpace(model.Code))
      {
        validationFailures.Add("Code required");
      }
      else if (!this.IsCodeValid(model.StockType, model.Code))
      {
        validationFailures.Add("Code invalid");
      }

      if (String.IsNullOrWhiteSpace(model.Description))
      {
        validationFailures.Add("Description required");
      }

      if (String.IsNullOrWhiteSpace(model.Colour))
      {
        validationFailures.Add("Colour required");
      }

      return validationFailures;
    }

    private void RaiseExceptionForFailure(UpdateStationNumbersValidationResultFailure failure, String stockType)
    {
      switch (failure)
      {
        case UpdateStationNumbersValidationResultFailure.NoAssignmentsSubmitted:
          throw new ArgumentException("No assignments submitted");
        case UpdateStationNumbersValidationResultFailure.StockTypeCannotBeUpdated:
          throw new ArgumentException(String.Format("StockType '{0}' cannot have its station numbers updated", stockType));
        case UpdateStationNumbersValidationResultFailure.StationNumberInvalid:
          throw new Exception("Station number invalid");
        case UpdateStationNumbersValidationResultFailure.StationNumberDuplicated:
          throw new Exception("Station number duplicated");
        default:
          throw new Exception("Unsupported validation failure");
      }
    }

    private UpdateStationNumbersValidationResult UpdateStationNumbersValidityCheck(
      Int32 mediaDefinitionId,
      String stockType,
      List<StationNumberAssignmentViewModel> assignments,
      out List<MediaStockEntity> existingMediaStockItems)
    {
      existingMediaStockItems = null;

      if (!assignments.Any())
      {
        return new UpdateStationNumbersValidationResult(false, UpdateStationNumbersValidationResultFailure.NoAssignmentsSubmitted);
      }

      if (stockType == "LaserStock" || stockType == "Outer")
      {
        // Station numbers are fixed for these stock types
        return new UpdateStationNumbersValidationResult(false, UpdateStationNumbersValidationResultFailure.StockTypeCannotBeUpdated);
      }

      existingMediaStockItems =
        this.mediaStockService.GetByMediaDefinition(mediaDefinitionId)
          .Where(ms => ms.StockType == stockType)
          .ToList();

      var mediaStockStationNumbers = new Dictionary<Int32, Int32>();
      foreach (var mediaStock in existingMediaStockItems)
      {
        mediaStockStationNumbers.Add(mediaStock.MediaStockId, mediaStock.StationNumber);
      }

      foreach (StationNumberAssignmentViewModel assignment in assignments)
      {
        var mediaStock = existingMediaStockItems.Single(ms => ms.MediaStockId == assignment.MediaStockId);

        if (assignment.StationNumber != mediaStock.StationNumber)
        {
          Boolean stationNumberIsValid = this.IsStationNumberValid(stockType, mediaStock.Code, assignment.StationNumber);
          if (!stationNumberIsValid)
          {
            return new UpdateStationNumbersValidationResult(false, UpdateStationNumbersValidationResultFailure.StationNumberInvalid);
          }

          mediaStockStationNumbers[mediaStock.MediaStockId] = assignment.StationNumber;
        }
      }

      Boolean hasDuplicates = existingMediaStockItems.Count != mediaStockStationNumbers.Values.Distinct().Count();

      if (hasDuplicates)
      {
        return new UpdateStationNumbersValidationResult(false, UpdateStationNumbersValidationResultFailure.StationNumberDuplicated);
      }

      return new UpdateStationNumbersValidationResult(true);
    }

    private List<MediaStockEntity> PerformUpdateStationNumbers(
      Int32 mediaDefinitionId,
      List<MediaStockEntity> existingMediaStockItems,
      IEnumerable<StationNumberAssignmentViewModel> assignments)
    {
      var stationNumberUpdates = new List<StationNumberAssignment>();

      foreach (StationNumberAssignmentViewModel assignment in assignments)
      {
        MediaStockEntity mediaStock = existingMediaStockItems.Single(ms => ms.MediaStockId == assignment.MediaStockId);

        if (mediaStock.StationNumber != assignment.StationNumber)
        {
          stationNumberUpdates.Add(
            new StationNumberAssignment
              {
                MediaStockID = assignment.MediaStockId,
                StationNumber = assignment.StationNumber,
                StockId = mediaStock.StockId,
                Code = mediaStock.Code,
                Description = mediaStock.Description
              });

          mediaStock.StationNumber = assignment.StationNumber;
        }
      }

      this.mediaStockService.UpdateStationNumbers(mediaDefinitionId, stationNumberUpdates);

      List<MediaStockEntity> orderedMediaStockItems = existingMediaStockItems.OrderBy(ms => ms.StationNumber).ToList();

      return orderedMediaStockItems;
    }

    private UpdateStationNumbersValidationResult AddMediaStockValidityCheck(
      Int32 mediaDefinitionId,
      String stockType,
      IEnumerable<AssignStockViewModel> stockToAdd,
      out List<MediaStockEntity> existingMediaStockItems)
    {
      var stationNumbersToAdd = new List<Int32>();

      existingMediaStockItems =
        this.mediaStockService.GetByMediaDefinition(mediaDefinitionId)
          .Where(ms => ms.StockType == stockType)
          .ToList();

      List<StockEntity> stockItemsToAdd =
        this.stockService.GetStockByType(stockType, String.Empty)
          .Where(s => stockToAdd.Any(sta => sta.StockId == s.StockId))
          .ToList();

      foreach (AssignStockViewModel stock in stockToAdd)
      {
        String code = stockItemsToAdd.Single(s => s.StockId == stock.StockId).Code;
        Boolean stationNumberIsValid = this.IsStationNumberValid(stockType, code, stock.StationNumber);
        if (!stationNumberIsValid)
        {
          return new UpdateStationNumbersValidationResult(false, UpdateStationNumbersValidationResultFailure.StationNumberInvalid);
        }

        stationNumbersToAdd.Add(stock.StationNumber);
      }

      List<Int32> allStationNumbers =
        existingMediaStockItems.Select(i => i.StationNumber)
          .Concat(stationNumbersToAdd)
          .ToList();

      Boolean hasDuplicates = allStationNumbers.Count != allStationNumbers.Distinct().Count();

      if (hasDuplicates)
      {
        return new UpdateStationNumbersValidationResult(false, UpdateStationNumbersValidationResultFailure.StationNumberDuplicated);
      }

      return new UpdateStationNumbersValidationResult(true);
    }

    private List<MediaStockEntity> PerformAddMediaStock(
      Int32 mediaDefinitionId,
      IEnumerable<MediaStockEntity> existingMediaStockItems,
      IEnumerable<AssignStockViewModel> stockToAdd)
    {
      var mediaStockItems = new List<MediaStockEntity>(existingMediaStockItems);

      foreach (AssignStockViewModel stock in stockToAdd)
      {
        MediaStockEntity createdMediaStockItem = this.mediaStockService.AddMediaStock(stock.StockId, mediaDefinitionId, stock.StationNumber);
        mediaStockItems.Add(createdMediaStockItem);
      }

      mediaStockItems = mediaStockItems.OrderBy(ms => ms.StationNumber).ToList();

      return mediaStockItems;
    }

    private List<StockEntity> GetAvailableStockItems(String stockType, IEnumerable<Int32> excludedStockIds, String filter)
    {
      if (filter == String.Empty)
      {
        return new List<StockEntity>();
      }

      List<StockEntity> availableStock = this.stockService.GetStockByType(stockType, filter);

      availableStock = availableStock.Where(s => !excludedStockIds.Any(i => s.StockId == i)).ToList();

      return availableStock;
    }

    private String GetStockTypeDisplayText(String stockType)
    {
      switch (stockType)
      {
        case "LaserStock":
          return Resources.JobsAdmin.Index.Trays;
        case "Insert":
          return Resources.JobsAdmin.Index.Inserts;
        case "Outer":
          return Resources.JobsAdmin.Index.Envelopes;
        default:
          throw new ArgumentException(String.Format("StockType '{0}' not supported", stockType));
      }
    }

    private String GetStockTypeDisplayTextSingular(String stockType)
    {
      switch (stockType)
      {
        case "LaserStock":
          return Resources.JobsAdmin.Index.Tray;
        case "Insert":
          return Resources.JobsAdmin.Index.Insert;
        case "Outer":
          return Resources.JobsAdmin.Index.Envelope;
        default:
          throw new ArgumentException(String.Format("StockType '{0}' not supported", stockType));
      }
    }

    private Boolean StationNumbersFixed(String stockType)
    {
      return stockType == "LaserStock" || stockType == "Outer";
    }

    private Boolean AreStockCodesRestricted(String stockType)
    {
      return stockType == "LaserStock";
    }

    private List<String> GetAvailableStockCodes(String stockType)
    {
      if (stockType == "LaserStock")
      {
        return new List<String> { "STATA", "STATB", "STATC", "STATD", "STATE", "STATF" };
      }
      
      // Only LaserStock has a restricted set of stock codes
      return new List<String>();
    }

    private Boolean IsCodeValid(String stockType, String code)
    {
      if (String.IsNullOrWhiteSpace(code))
      {
        return false;
      }

      if (this.AreStockCodesRestricted(stockType))
      {
        return this.GetAvailableStockCodes(stockType).Contains(code);
      }

      return true;
    }

    private Boolean IsStationNumberValid(String stockType, String code, Int32 stationNumber)
    {
      if (stationNumber < 0)
      {
        return false;
      }

      switch (stockType)
      {
        case "LaserStock":
          switch (code)
          {
            case "STATA":
              return stationNumber == 1;
            case "STATB":
              return stationNumber == 2;
            case "STATC":
              return stationNumber == 3;
            case "STATD":
              return stationNumber == 4;
            case "STATE":
              return stationNumber == 5;
            case "STATF":
              return stationNumber == 6;
          }

          return false;

        case "Insert":
          const Int32 MaxStationNumberForInserts = 9;
          return stationNumber > 0 && stationNumber <= MaxStationNumberForInserts;
        case "Outer":
          return stationNumber == 0;
        default:
          throw new ArgumentException("stockType not supported");
      }
    }

    private class UpdateStationNumbersValidationResult
    {
      public UpdateStationNumbersValidationResult(Boolean result, UpdateStationNumbersValidationResultFailure failure)
      {
        if (result)
        {
          throw new ArgumentException("Result must be false for this overload");
        }

        this.Result = false;
        this.Failure = failure;
      }

      public UpdateStationNumbersValidationResult(Boolean result)
      {
        if (!result)
        {
          throw new ArgumentException("Result must be true for this overload");
        }

        this.Result = true;
      }

      public Boolean Result { get; private set; }

      public UpdateStationNumbersValidationResultFailure Failure { get; private set; }
    }
  }
}